#include "common.h"
#include "Arduino.h"
#include <EEPROM.h>
#include <Adafruit_GFX.h>
#include <ST7735.h>
#include "OneButton.h"
#include <stm32g0xx.h>
#include <HardwareTimer.h>

// 使用TIM17定时器
HardwareTimer timer(TIMUSED);
// LCD
SPIClass SPI_2(LCD_MOSI, LCD_MISO, LCD_SCK, -1);
ST7735 tft = ST7735(&SPI_2, -1, LCD_DC, LCD_RES);
// key
OneButton btnPower(POWER_S, true);
OneButton btn2(KEY2, true);
OneButton btn3(KEY3, true);

// MENU
// 菜单选项，取值123，切换菜单是设置为对应负值
int8_t menu = -1;
#define menuRPM 1
#define menuCTR 2
#define menuIR 3

int8_t isSetting = 0;
int8_t isBtnPowerClick = false;
int8_t isBtn2Click = false;
int8_t isBtn3Click = false;
int8_t isbtn2LongPress = false;

// 是否激光模式 true激光 false霍尔
int8_t isLD = true;
// 是否启动
int8_t isStart = false;
// 计数器 计数统计
uint32_t count = 0;
// 定时器溢出计数统计
uint32_t overflowCount = 0;
// 频率计 记录上次触发时间(毫秒部分)
uint32_t lastTimeMs = 0;
// 频率计 记录上次触发时间(微秒部分次数)
int32_t lastTimeUsCount = 0;
// 频率计 触发间隔,用于计算频率
const uint32_t tickLoad = SysTick->LOAD + 1;
float delayTime = 0;
int32_t measuredDelayMs = 0;
int32_t measuredDelayNsCount = 0;
float rps = 0.0;
float rpsMax = 0.0;

// 计数器模式 启动时间
int startTime = 0;
unsigned long runningTime;
bool isPowerOn = false;
// 自动关机时间变量
long shutdownTime;

int8_t filterPos = 0;
uint32_t measuredDelays[FILTER_WINDOW_SIZE];

struct Config {
  uint32_t version;     // 版本号，与默认值不同时会用默认值覆盖所有配置
  uint8_t lcdInitType;  // 屏幕类型
  // uint8_t lcdBacklight;       // 背光亮度
  uint8_t invertDisplay;       // 屏幕是否反色
  float lowBat;                // 电量下限，电池框显示没电
  float fullBat;               // 电量上限，电池框显示满电
  uint32_t shutdownCountdown;  // 关机倒计时（秒）
  uint8_t LDSignalInvet;       // 激光接收管信号是否翻转
  uint8_t HLSignalInvet;       // 霍尔接收管信号是否翻转
  uint8_t showStart;           // 显示开机动画
  uint8_t showEnd;             // 显示关机动画
  uint32_t TFTrefreshCycle;    // 屏幕刷新周期(毫秒)
  uint8_t LCD_RGB;             // RGB/BGR
  uint16_t calibrationValue;   // 校准值,测量32768数据源数值
};

// 需要与Config顺序一致(排除version)
enum ConfigItemIndex {
  IDX_LCD_INIT_TYPE,
  // IDX_LCD_BACKLIGHT,
  IDX_INVERT_DISPLAY,
  IDX_LOW_BAT,
  IDX_FULL_BAT,
  IDX_SHUTDOWN_COUNTDOWN,
  IDX_LD_SIGNAL_INVET,
  IDX_HL_SIGNAL_INVET,
  IDX_SHOW_START,
  IDX_SHOW_END,
  IDX_LCD_REFRESH_CYCLE,
  IDX_LCD_RGB,
  IDX_CALIBRATION_VALUE,

  IDX_SAVE,
  IDX_EXIT,
  IDX_DEFAULT,
  CONFIG_ITEM_COUNT
};

const char *menuItems[CONFIG_ITEM_COUNT] = {
  [IDX_LCD_INIT_TYPE] = "lcdInitType",
  // [IDX_LCD_BACKLIGHT] = "lcdBacklight",
  [IDX_INVERT_DISPLAY] = "invertDisplay",
  [IDX_LOW_BAT] = "lowBat",
  [IDX_FULL_BAT] = "fullBat",
  [IDX_SHUTDOWN_COUNTDOWN] = "shutdownCountdown",
  [IDX_LD_SIGNAL_INVET] = "LDSignalInvet",
  [IDX_HL_SIGNAL_INVET] = "HLSignalInvet",
  [IDX_SHOW_START] = "showStart",
  [IDX_SHOW_END] = "showEnd",
  [IDX_LCD_REFRESH_CYCLE] = "LCDrefreshCycle",
  [IDX_LCD_RGB] = "LCD RGB",
  [IDX_CALIBRATION_VALUE] = "calibration",

  [IDX_SAVE] = "Save",
  [IDX_EXIT] = "Exit",
  [IDX_DEFAULT] = "Reset",

};

Config defaultConfig = {
  CONFIG_VERSION,
  LCD_INIT_TYPE_DEFAULT,
  // LCD_BACKLIGHT_DEFAULT,
  LCD_INVERT_DISPLAY_DEFAULT,
  LOW_BAT_DEFAULT,
  FULL_BAT_DEFAULT,
  SHUTDOWN_COUNTDOWN_DEFAULT,
  LD_SIGNAL_INVET_DEFAULT,
  HL_SIGNAL_INVET_DEFAULT,
  SHOW_START_DEFAULT,
  SHOW_END_DEFAULT,
  LCD_REFRESH_CYCLE_DEFAULT,
  ST7735_MADCTL_BGR,
  CALIBRATION_VALUE_DEFAULT
};
Config config;

#if defined ENABLE_HSE
// 启用外部晶振
void SystemClock_Config(void) {
  RCC_OscInitTypeDef RCC_OscInitStruct = {};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {};

  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  // 启用HSE并配置PLL
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;  // 启用外部晶振
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;  // PLL源改为HSE
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;           // HSE不分频 (8MHz)
  RCC_OscInitStruct.PLL.PLLN = 16;                      // 倍频至128MHz (8MHz * 16)
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;           // 分频得64MHz系统时钟
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler();
  }

  // 时钟树配置保持不变
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;  // 系统时钟源仍为PLL
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;         // AHB时钟=64MHz
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;          // APB时钟=64MHz

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
    Error_Handler();
  }
}
#endif


/**
 * @brief  初始化输入捕获定时器
 * @param  signalInvet: 捕获信号翻转，true为捕获上升沿，false为捕获下降沿
 * @retval None
 */
void initTIM(bool signalInvet) {
  // 停止定时器
  timer.pause();

  // 16分频
  timer.setPrescaleFactor(16);

  timer.setOverflow(TIM_OVERFLOW_VAL - 1, TICK_FORMAT);

  // 设置输入捕获
  timer.setMode(1, signalInvet ? TIMER_INPUT_CAPTURE_RISING : TIMER_INPUT_CAPTURE_FALLING, SIGNAL, FILTER_CKINT_N2);
  // timer.setMode(1, TIMER_INPUT_CAPTURE_RISING,SIGNAL, FILTER_CKINT_N2);

  // 清除可能存在的挂起中断
  timer.refresh();

  // 输入捕获处理
  timer.attachInterrupt(1, timerCaptureHandler);
  // 定时器溢出处理
  timer.attachInterrupt(timerOverflowHandler);

  // 启动定时器
  timer.resume();
}
void timerCaptureHandler() {
  static uint32_t last_capture = 0;
  static uint32_t c;
  uint32_t current_capture = timer.getCaptureCompare(1);
  uint32_t o = overflowCount - c;
  c = overflowCount;
  filterPos++;
  if (filterPos >= FILTER_WINDOW_SIZE)
    filterPos = 0;
  measuredDelays[filterPos] = TIM_OVERFLOW_VAL * o + current_capture - last_capture;
  last_capture = current_capture;
  lastTimeMs = millis();

  count++;
}

void timerOverflowHandler() {
  overflowCount++;
}
// GPIO初始化
void initPin() {
  // Serial.println("initPin");

  pinMode(POWER_C, OUTPUT);
  pinMode(POWER_S, INPUT_PULLUP);
  pinMode(KEY2, INPUT_PULLUP);
  pinMode(KEY3, INPUT_PULLUP);

  pinMode(SIGNAL, INPUT_PULLUP);
  pinMode(LD_CTRL, OUTPUT);

  pinMode(LD_POWER, OUTPUT);
  pinMode(HL_POWER, OUTPUT);
  pinMode(LCD_BLK, OUTPUT);
  pinMode(BAT_ADC, INPUT);
  pinMode(LCD_CS, OUTPUT);
  pinMode(CHARGE, INPUT);
  pinMode(LCD_RES, OUTPUT);

  digitalWrite(POWER_C, LOW);
  digitalWrite(LD_CTRL, LOW);
  digitalWrite(LD_POWER, HIGH);
  digitalWrite(HL_POWER, HIGH);
  digitalWrite(LCD_BLK, LOW);
  digitalWrite(LCD_CS, LOW);
  digitalWrite(LCD_RES, HIGH);
}
// 电源键单击
void btnPowerClick() {
  shutdownTime = millis();
  isBtnPowerClick = true;
}
uint8_t getBtnPowerClick() {
  uint8_t b = isBtnPowerClick;
  isBtnPowerClick = false;
  return b;
}
// 电源键长按
void btnPowerLongPress() {
  shutdownTime = millis();
  isPowerOn = !isPowerOn;
  if (isPowerOn) {
    NVIC_SystemReset();
    while (1)
      ;
  } else
    powerOff();
}
void btn2Click() {
  shutdownTime = millis();
  isBtn2Click = true;
}
uint8_t getBtn2Click() {
  uint8_t b = isBtn2Click;
  isBtn2Click = false;
  return b;
}
void btn2LongPress() {
  isbtn2LongPress = true;
}
void btn3Click() {
  shutdownTime = millis();
  isBtn3Click = true;
}
uint8_t getBtn3Click() {
  uint8_t b = isBtn3Click;
  isBtn3Click = false;
  return b;
}
// 电源键双击 进入设置
void btn3LongPress() {
  if (isSetting == 0)
    isSetting = -1;
  else
    isSetting = 0;
}
// 屏幕初始化
void initLCD() {
  // Serial.println("initLCD");

  if (config.lcdInitType > LCD_INIT_TYPE_MAX)
    config.lcdInitType = LCD_INIT_TYPE_DEFAULT;
  switch (config.lcdInitType) {
    case 0:
      tft.initR(INITR_MINI160x80);
      break;
    case 1:
      tft.initR(INITR_MINI160x80_PLUGIN);
      break;
    default:
      tft.initR(INITR_MINI160x80_PLUGIN);
  }
  tft.setRotation(1);
  // 设置RGB
  uint8_t madctl = 0;
  madctl = ST77XX_MADCTL_MY | ST77XX_MADCTL_MV | config.LCD_RGB;
  tft.sendCommand(ST77XX_MADCTL, &madctl, 1);

  // 屏幕反色
  tft.invertDisplay(config.invertDisplay);

  // tft.setTextWrap(true);
  tft.setFont(NULL);

  tft.fillScreen(ST7735_BLACK);
}

// 开机
void powerOn() {
  // Serial.println("powerOn");

  isPowerOn = true;
  digitalWrite(LCD_BLK, HIGH);
  digitalWrite(POWER_C, HIGH);
}

// 关机
void powerOff() {
  // Serial.println("powerOff");
  stopHALL();
  stopLD();
  if (config.showEnd)
    shutdownAnimation();
  digitalWrite(LCD_BLK, LOW);
  digitalWrite(POWER_C, LOW);
  delay(100);
}
void startLD() {
  // Serial.println("startLD");
  digitalWrite(LD_CTRL, HIGH);
  digitalWrite(LD_POWER, LOW);
  filterPos = 0;
  memset(measuredDelays, 0, FILTER_WINDOW_SIZE);
  count = 0;
  initTIM(config.LDSignalInvet);
}

void stopLD() {
  // Serial.println("stopLD");
  digitalWrite(LD_CTRL, LOW);
  digitalWrite(LD_POWER, HIGH);
  timer.pause();
}

void startHALL() {
  // Serial.println("startHALL");
  digitalWrite(HL_POWER, LOW);
  filterPos = 0;
  memset(measuredDelays, 0, FILTER_WINDOW_SIZE);
  count = 0;
  initTIM(config.HLSignalInvet);
}

void stopHALL() {
  // Serial.println("stopHALL");
  digitalWrite(HL_POWER, HIGH);
  timer.pause();
}

void stop(int menu) {
  isStart = false;
  stopLD();
  stopHALL();
}

// 刷新电池图标
void refreshBat() {
  uint16_t analogValue = analogRead(BAT_ADC);
  float v = 6.6 * analogValue / 1024;
  if (v < config.lowBat)
    v = config.lowBat;
  else if (v > config.fullBat)
    v = config.fullBat;
  // 分段电量值
  int8_t percentage = map(v * 1000, config.lowBat * 1000, config.fullBat * 1000, 0, 5);

  static int8_t lastPercentage = -1;
  if (percentage != lastPercentage) {
    lastPercentage = percentage;
    tft.fillRect(BATTERY_ICON_LOCATION_X, BATTERY_ICON_LOCATION_Y + 2, 2, 8, ST7735_GREEN);
    tft.drawRect(BATTERY_ICON_LOCATION_X + 2, BATTERY_ICON_LOCATION_Y, 23, 12, ST7735_GREEN);
    tft.drawRect(BATTERY_ICON_LOCATION_X + 3, BATTERY_ICON_LOCATION_Y + 1, 21, 10, 0x4208);
    for (uint8_t i = 0; i < 5; i++) {
      tft.fillRect(BATTERY_ICON_LOCATION_X + 4 + i * 4, BATTERY_ICON_LOCATION_Y + 2, 3, 8, percentage >= (5 - i) ? ST7735_GREEN : 0x4208);
      tft.drawLine(BATTERY_ICON_LOCATION_X + 7 + i * 4, BATTERY_ICON_LOCATION_Y + 2, BATTERY_ICON_LOCATION_X + 7 + i * 4, BATTERY_ICON_LOCATION_Y + 10, 0x4208);
    }
  }
}
// 菜单切换
void switchMenu(int menu) {
  // tft.fillScreen(ST7735_BLACK);
  tft.fillRect(0, 0, 120, 24, ST7735_BLACK);
  tft.drawRect(0, 0, 41, 24, ST7735_GREEN);
  tft.drawRect(40, 0, 41, 24, ST7735_GREEN);
  tft.drawRect(80, 0, 41, 24, ST7735_GREEN);
  tft.fillRect((menu - 1) * 40, 0, 41, 24, ST7735_GREEN);

  tft.setTextSize(2);

  tft.setTextColor(menu == 1 ? ST7735_BLACK : ST7735_GREEN);
  tft.setCursor(3, 5);
  tft.print("RPM");
  tft.setTextColor(menu == 2 ? ST7735_BLACK : ST7735_GREEN);
  tft.setCursor(43, 5);
  tft.print("CTR");
  tft.setTextColor(menu == 3 ? ST7735_BLACK : ST7735_GREEN);
  tft.setCursor(88, 5);
  tft.print("IR");

  tft.fillRect(0, 25, 160, 55, ST7735_BLACK);
}
// 刷新RPM界面
void refreshRPM() {
  float rps;
  char buffer[7];  // 6位数字 + 1位字符串结束符 '\0'
  long lRps;       // 四舍五入取整
  long ms = millis();
  uint32_t sortMeasuredDelays[FILTER_WINDOW_SIZE];
  if (measuredDelays[filterPos] == 0) {
    rps = 0;
  }
  // 16分频,200000对应50ms，既20RPS，低于20RPS直接输出，高于20RPS滤波
  else if (measuredDelays[filterPos] > 200000 || count < FILTER_WINDOW_SIZE) {
    // rps = 1000000.0 / measuredDelays[filterPos] / 0.25;
    rps = 4000000.0 / measuredDelays[filterPos] * 32768 / config.calibrationValue;
  }
  // 滤波，数组排序后取中位数
  else {
    std::partial_sort_copy(measuredDelays, measuredDelays + FILTER_WINDOW_SIZE,
                           sortMeasuredDelays, sortMeasuredDelays + FILTER_WINDOW_SIZE);
    rps = 4000000.0 / sortMeasuredDelays[FILTER_WINDOW_SIZE / 2] * 32768 / config.calibrationValue;
  }

  if (rps > 99999)
    rps = 99999;
  if ((rps * 60) < 6)
    rps = 0;

  // 最低频率限制 6rpm,小于6rpm时为0
  if ((lastTimeMs + 1000 * 10) < ms)
    rps = 0;

  if (rpsMax < rps)
    rpsMax = rps;
  if (!isStart) {
    rps = rpsMax;
  }
  // tft.setFont(&FreeSerifBold12pt7b);
  tft.setTextSize(2);
  tft.setTextColor(ST7735_YELLOW, ST7735_BLACK);
  tft.setCursor(0, 40);
  if (isStart)
    tft.print("RPM:");
  else
    tft.print("MAX:");

  tft.setTextColor(ST7735_MAGENTA, ST7735_BLACK);
  tft.setCursor(0, 65);
  tft.print("RPS:");

  tft.setTextColor(ST7735_MAGENTA, ST7735_BLACK);
  tft.setCursor(52, 65);
  lRps = (long)round(rps);
  sprintf(buffer, "%05d", lRps);
  //  tft.fillRect(45, 60, 115, 20, ST7735_BLACK);
  tft.print(buffer);

  // tft.setFont(&FreeSerifBold18pt7b);
  tft.setTextSize(3);
  tft.setCursor(52, 35);
  lRps = (long)round(rps * 60);
  // 上限999999,超过999999显示999999红色
  tft.setTextColor(lRps < RPM_DISPLAY_UPPER_LIMIT ? ST7735_YELLOW : ST7735_RED, ST7735_BLACK);
  if (lRps > RPM_DISPLAY_UPPER_LIMIT) {
    lRps = RPM_DISPLAY_UPPER_LIMIT;
  }
  sprintf(buffer, "%06d", lRps);
  //  tft.fillRect(45, 30, 115, 30, ST7735_BLACK);
  tft.print(buffer);

  tft.setTextSize(2);
  tft.setTextColor(isStart ? ST7735_GREEN : ST7735_RED, ST7735_BLACK);
  tft.setCursor(135, 65);
  tft.print(isLD ? "LD" : "HL");
  // Serial.print("refreshRPM:");
  // Serial.println(millis() - ms);
}

void refreshCTR() {
  if (isStart) {
    runningTime = millis() / 1000 - startTime / 1000;
  }
  char buffer[9];  // 6位数字 + 1位字符串结束符 '\0'

  tft.setTextSize(2);
  tft.setTextColor(ST7735_YELLOW, ST7735_BLACK);
  tft.setCursor(0, 40);
  tft.print("CTR:");

  int hours = runningTime / 3600;
  int remainingSeconds = runningTime % 3600;
  int minutes = remainingSeconds / 60;
  int seconds = remainingSeconds % 60;
  sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
  tft.setTextColor(ST7735_MAGENTA, ST7735_BLACK);
  tft.setCursor(0, 65);
  tft.print(buffer);

  tft.setTextSize(3);
  //  tft.setTextColor(ST7735_YELLOW,ST7735_BLACK);
  tft.setTextColor(count < RPM_DISPLAY_UPPER_LIMIT ? ST7735_YELLOW : ST7735_RED, ST7735_BLACK);
  tft.setCursor(52, 35);
  sprintf(buffer, "%06d", count < RPM_DISPLAY_UPPER_LIMIT ? count : RPM_DISPLAY_UPPER_LIMIT);
  tft.print(buffer);

  tft.setTextSize(2);
  tft.setTextColor(isStart ? ST7735_GREEN : ST7735_RED, ST7735_BLACK);
  tft.setCursor(135, 65);
  tft.print(isLD ? "LD" : "HL");
}

void btnHandler() {
  if (isSetting != 0) {
    if (getBtnPowerClick()) {
      if (isSetting > 0)
        isSetting++;
      if (isSetting > CONFIG_ITEM_COUNT)
        isSetting = 1;
    }

    int8_t v = 0;
    if (getBtn2Click())
      v = 1;
    else if (getBtn3Click()) {
      v = -1;
    }
    if (v != 0) {
      switch (abs(isSetting) - 1) {
        case IDX_LCD_INIT_TYPE:
          config.lcdInitType += v;
          if (config.lcdInitType == 255)
            config.lcdInitType = LCD_INIT_TYPE_MAX;
          if (config.lcdInitType > LCD_INIT_TYPE_MAX)
            config.lcdInitType = 0;
          break;
        // case IDX_LCD_BACKLIGHT:
        //     config.lcdBacklight += 10 * v;
        //     if (config.lcdBacklight > LCD_BACKLIGHT_MAX)
        //         config.lcdBacklight = LCD_BACKLIGHT_MAX;
        //     else if (config.lcdBacklight < LCD_BACKLIGHT_MIN)
        //         config.lcdBacklight = LCD_BACKLIGHT_MIN;
        //     break;
        case IDX_INVERT_DISPLAY:
          config.invertDisplay = !config.invertDisplay;
          break;
        case IDX_LOW_BAT:
          config.lowBat += 0.01 * v;
          if (config.lowBat > LOW_BAT_MAX)
            config.lowBat = LOW_BAT_MAX;
          else if (config.lowBat < LOW_BAT_MIN)
            config.lowBat = LOW_BAT_MIN;
          break;
        case IDX_FULL_BAT:
          config.fullBat += 0.01 * v;
          if (config.fullBat > FULL_BAT_MAX)
            config.fullBat = FULL_BAT_MAX;
          else if (config.fullBat < FULL_BAT_MIN)
            config.fullBat = FULL_BAT_MIN;
          break;
        case IDX_SHUTDOWN_COUNTDOWN:
          config.shutdownCountdown += 10 * v;
          if (config.shutdownCountdown > SHUTDOWN_COUNTDOWN_MAX)
            config.shutdownCountdown = SHUTDOWN_COUNTDOWN_MAX;
          else if (config.shutdownCountdown < SHUTDOWN_COUNTDOWN_MIN)
            config.shutdownCountdown = SHUTDOWN_COUNTDOWN_MIN;
          break;
        case IDX_LD_SIGNAL_INVET:
          config.LDSignalInvet = !config.LDSignalInvet;
          break;
        case IDX_HL_SIGNAL_INVET:
          config.HLSignalInvet = !config.HLSignalInvet;
          break;
        case IDX_SHOW_START:
          config.showStart = !config.showStart;
          break;
        case IDX_SHOW_END:
          config.showEnd = !config.showEnd;
          break;
        case IDX_LCD_REFRESH_CYCLE:
          config.TFTrefreshCycle += 100 * v;
          if (config.TFTrefreshCycle > LCD_REFRESH_CYCLE_MAX)
            config.TFTrefreshCycle = LCD_REFRESH_CYCLE_MAX;
          else if (config.TFTrefreshCycle < LCD_REFRESH_CYCLE_MIN)
            config.TFTrefreshCycle = LCD_REFRESH_CYCLE_MIN;
          break;
        case IDX_LCD_RGB:
          config.LCD_RGB = config.LCD_RGB == ST77XX_MADCTL_RGB ? ST7735_MADCTL_BGR : ST77XX_MADCTL_RGB;
          break;
        case IDX_CALIBRATION_VALUE:
          config.calibrationValue = config.calibrationValue + v;
          if (config.calibrationValue > CALIBRATION_VALUE_MAX)
            config.calibrationValue = CALIBRATION_VALUE_MAX;
          else if (config.calibrationValue < CALIBRATION_VALUE_MIN)
            config.calibrationValue = CALIBRATION_VALUE_MIN;
          break;
        case IDX_SAVE:
          configSave(config);
          NVIC_SystemReset();
          while (1)
            ;
        case IDX_EXIT:
          NVIC_SystemReset();
          while (1)
            ;
        case IDX_DEFAULT:
          configSave(defaultConfig);
          NVIC_SystemReset();
          while (1)
            ;
      }
    }
  } else {
    if (getBtnPowerClick()) {
      menu++;
      if (menu > 3)
        menu = 1;
      menu = -menu;
    }
    // 菜单切换
    if (menu < 0) {
      menu = -menu;
      switchMenu(menu);

      isStart = false;
      stop(menu);

      switch (menu) {
        case 1:
          rps = 0;
          delayTime = 100000000;
          rpsMax = 0;
          break;
        case 2:
          count = 0;
          startTime = millis();
          runningTime = 0;
      }
    }

    // RPM页面按键处理
    if (menu == 1) {
      if (getBtn2Click()) {
        isLD = !isLD;
        if (isStart) {
          if (isLD) {
            stopHALL();
            startLD();
          } else {
            stopLD();
            startHALL();
          }
        }
      }
      if (getBtn3Click()) {
        isStart = !isStart;
        if (isStart) {
          rps = 0;
          rpsMax = 0;
          if (isLD)
            startLD();
          else
            startHALL();
        } else {
          stop(menu);
        }
      }
      // CTR页面按键处理
    } else if (menu == 2) {
      if (getBtn2Click()) {
        isLD = !isLD;
        if (isStart) {
          if (isLD) {
            stopHALL();
            startLD();
          } else {
            stopLD();
            startHALL();
          }
        }
      }
      if (getBtn3Click()) {
        isStart = !isStart;
        if (isStart) {
          count = 0;
          startTime = millis();
          if (isLD)
            startLD();
          else
            startHALL();
        } else {
          stop(menu);
        }
      }
    } else if (menu == 3) {
    }
    // btn2长按，切换屏幕初始化参数
    if (isbtn2LongPress) {
      isbtn2LongPress = false;
      config.lcdInitType++;
      if (config.lcdInitType > LCD_INIT_TYPE_MAX)
        config.lcdInitType = 0;
      EEPROM.put(0, config);
      initLCD();
      switchMenu(menu);
    }
  }
}
void loading() {
  // 绘制静态元素
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(1);
  tft.setCursor(45, 20);
  tft.print("LOADING...");

  // 进度条外框
  tft.drawRect(20, 40, 120, 15, ST77XX_WHITE);

  // 动态进度条动画
  for (int i = 0; i <= 100; i += 2) {
    int barWidth = map(i, 0, 100, 0, 118);             // 计算填充长度
    tft.fillRect(21, 41, barWidth, 13, ST77XX_GREEN);  // 绿色填充

    // 显示百分比
    tft.fillRect(65, 60, 30, 10, ST77XX_BLACK);  // 清除旧文本
    tft.setCursor(65, 60);
    tft.print(i);
    tft.print("%");

    delay(10);  // 控制动画速度
  }
  tft.fillScreen(ST77XX_BLACK);
}

void shutdownAnimation() {
  int w = 160, h = 80;  // 屏幕尺寸
  int steps = 24;       // 动画步数（更多步数更平滑）

  // 2. 光晕收缩效果
  for (int i = 0; i < steps; i++) {
    float progress = (float)i / steps;       // 0.0~1.0
    float curve = 1.0 - pow(progress, 0.7);  // 缓动曲线

    // 计算光晕半径
    int radius = (int)(min(w, h) * 0.5 * curve);

    // 中心坐标
    int centerX = w / 2;
    int centerY = h / 2;

    // 清屏（保留中心光晕）
    tft.fillScreen(ST77XX_BLACK);

    // 绘制光晕（白色到黑色的径向渐变）
    for (int r = radius; r > 0; r -= 2) {
      uint8_t intensity = 255 * (float)r / radius;
      uint16_t color = tft.color565(intensity, intensity, intensity);
      tft.drawCircle(centerX, centerY, r, color);
    }

    // 中心亮点
    tft.fillCircle(centerX, centerY, 2, ST77XX_WHITE);

    delay(30);  // 流畅动画
  }

  // 最终全黑
  tft.fillScreen(ST77XX_BLACK);
}
void configSave(Config config) {
  // Serial.println("configSave");
  int state = digitalRead(POWER_C);
  // 保存前拉高,确保保存过程中不会关机
  digitalWrite(POWER_C, HIGH);
  EEPROM.put(0, config);
  EEPROM.get(0, config);
  digitalWrite(POWER_C, state);
  // Serial.println("save success");
}
// 检查配置
void checkConfig() {
  bool isSave = false;
  EEPROM.get(0, config);
  // Serial.println("config:");
  // // 打印持久化配置
  // Serial.print("verson:");
  // Serial.println(config.version);
  // Serial.print("lcdInitType:");
  // Serial.println(config.lcdInitType);
  // // Serial.print("lcdBacklight:");
  // // Serial.println(config.lcdBacklight);
  // Serial.print("invertDisplay:");
  // Serial.println(config.invertDisplay);
  // Serial.print("lowBat:");
  // Serial.println(config.lowBat, 2);
  // Serial.print("fullBat:");
  // Serial.println(config.fullBat, 2);
  // Serial.print("shutdownCountdown:");
  // Serial.println(config.shutdownCountdown);
  // Serial.print("LDSignalInvet:");
  // Serial.println(config.LDSignalInvet);
  // Serial.print("HLSignalInvet:");
  // Serial.println(config.HLSignalInvet);

  // 检查是否需要调整
  if (config.version != defaultConfig.version) {
    config = defaultConfig;
    isSave = true;
  }
  if (config.lcdInitType > LCD_INIT_TYPE_MAX) {
    config.lcdInitType = defaultConfig.lcdInitType;
    isSave = true;
  }
  // if (config.lcdBacklight > LCD_BACKLIGHT_MAX || config.lcdBacklight < LCD_BACKLIGHT_MIN)
  // {
  //     config.lcdBacklight = defaultConfig.lcdBacklight;
  //     isSave = true;
  // }
  if (config.invertDisplay > 1) {
    config.invertDisplay = defaultConfig.invertDisplay;
    isSave = true;
  }
  if (config.lowBat > LOW_BAT_MAX || config.lowBat < LOW_BAT_MIN) {
    config.lowBat = defaultConfig.lowBat;
    isSave = true;
  }
  if (config.fullBat > FULL_BAT_MAX || config.fullBat < FULL_BAT_MIN) {
    config.fullBat = defaultConfig.fullBat;
    isSave = true;
  }
  if (config.shutdownCountdown > SHUTDOWN_COUNTDOWN_MAX || config.shutdownCountdown < SHUTDOWN_COUNTDOWN_MIN) {
    config.shutdownCountdown = defaultConfig.shutdownCountdown;
    isSave = true;
  }
  if (config.LDSignalInvet > 1) {
    config.LDSignalInvet = defaultConfig.LDSignalInvet;
    isSave = true;
  }
  if (config.HLSignalInvet > 1) {
    config.HLSignalInvet = defaultConfig.HLSignalInvet;
    isSave = true;
  }
  if (config.showStart > 1) {
    config.showStart = defaultConfig.showStart;
    isSave = true;
  }
  if (config.showEnd > 1) {
    config.showEnd = defaultConfig.showEnd;
    isSave = true;
  }
  if (config.TFTrefreshCycle > LCD_REFRESH_CYCLE_MAX || config.TFTrefreshCycle < LCD_REFRESH_CYCLE_MIN) {
    config.TFTrefreshCycle = defaultConfig.TFTrefreshCycle;
    isSave = true;
  }
  if (config.LCD_RGB != ST77XX_MADCTL_RGB && config.LCD_RGB != ST7735_MADCTL_BGR) {
    config.LCD_RGB = defaultConfig.LCD_RGB;
    isSave = true;
  }
  if (config.calibrationValue > CALIBRATION_VALUE_MAX || config.calibrationValue < CALIBRATION_VALUE_MIN) {
    config.calibrationValue = defaultConfig.calibrationValue;
    isSave = true;
  }
  if (isSave) {
    configSave(config);
  }
}
void powerOffHandler() {
  // 判断关机
  // CTR模式和充电时不自动关机
  if (menu == 2 || digitalRead(CHARGE) == 1)
    shutdownTime = millis();
  long sdTime = config.shutdownCountdown - (millis() - shutdownTime) / 1000;
  if (sdTime < 0)
    sdTime = 0;
  tft.drawLine(0, 26, 160, 26, ST7735_BLACK);
  tft.drawLine(0, 26, (int)(1.0 * sdTime / config.shutdownCountdown * 160), 26, ST7735_GREEN);

  if (menu != 2 && sdTime == 0) {
    powerOff();
  }
}
void refreshSetting() {
  static uint8_t lastPage = 0xFF;  // 初始值设为不可能的值
  static uint8_t lastSetting = 0xFF;
  static uint32_t lastValues[CONFIG_ITEM_COUNT] = { 0 };

  if (isSetting == 0)
    return;

  // 当前配置项
  uint8_t currentSetting = abs(isSetting);
  // 当前页码
  uint8_t page = (currentSetting - 1) / SETTINT_DISPLAY_ITEM_COUNT;
  // 总菜单项
  uint8_t total = CONFIG_ITEM_COUNT;
  // 当前页显示菜单数量
  uint8_t showSize = min(SETTINT_DISPLAY_ITEM_COUNT, total - page * SETTINT_DISPLAY_ITEM_COUNT);

  // 换页时清屏
  if (lastPage != page) {
    tft.fillScreen(ST7735_BLACK);
  }

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_WHITE, ST7735_BLACK);
  tft.setCursor(120, 0);
  tft.print(FIRMWARE_VERSION);
  for (uint8_t idx = 0; idx < showSize; idx++) {
    uint8_t currentIdx = page * SETTINT_DISPLAY_ITEM_COUNT + idx;
    uint16_t bgColor = (currentSetting - 1 == currentIdx) ? ST77XX_WHITE : ST7735_BLACK;
    uint16_t textColor = (currentSetting - 1 == currentIdx) ? ST7735_BLACK : ST77XX_WHITE;

    // 只刷新值变化或需要高亮的项
    uint32_t currentValue = 0;
    switch (currentIdx) {
      case IDX_LCD_INIT_TYPE:
        currentValue = config.lcdInitType;
        break;
      // case IDX_LCD_BACKLIGHT:
      //     currentValue = config.lcdBacklight;
      //     break;
      case IDX_INVERT_DISPLAY:
        currentValue = config.invertDisplay;
        break;
      case IDX_LOW_BAT:
        currentValue = (uint32_t)(config.lowBat * 100);
        break;
      case IDX_FULL_BAT:
        currentValue = (uint32_t)(config.fullBat * 100);
        break;
      case IDX_SHUTDOWN_COUNTDOWN:
        currentValue = config.shutdownCountdown;
        break;
      case IDX_LD_SIGNAL_INVET:
        currentValue = config.LDSignalInvet;
        break;
      case IDX_HL_SIGNAL_INVET:
        currentValue = config.HLSignalInvet;
        break;
      case IDX_SHOW_START:
        currentValue = config.showStart;
        break;
      case IDX_SHOW_END:
        currentValue = config.showEnd;
        break;
      case IDX_LCD_REFRESH_CYCLE:
        currentValue = config.TFTrefreshCycle;
        break;
      case IDX_LCD_RGB:
        currentValue = config.LCD_RGB;
        break;
      case IDX_CALIBRATION_VALUE:
        currentValue = config.calibrationValue;
        break;
    }

    if (lastValues[currentIdx] != currentValue || (currentSetting != lastSetting && (currentSetting - 1 == currentIdx || lastSetting - 1 == currentIdx)) || lastPage != page || isSetting < 0) {
      // 清除旧内容
      tft.fillRect(0, idx * 11, 160, 11, ST7735_BLACK);
      // 绘制新内容
      tft.setTextColor(textColor, bgColor);
      tft.setCursor(0, idx * 11);
      tft.print(menuItems[currentIdx]);
      if (currentIdx < CONFIG_ITEM_COUNT - 3) {
        tft.print(":");

        tft.setTextColor(ST77XX_WHITE, ST7735_BLACK);
        tft.print(" ");
        switch (currentIdx) {
          case IDX_LCD_INIT_TYPE:
            tft.print(config.lcdInitType);
            break;
          // case IDX_LCD_BACKLIGHT:
          //     tft.print(config.lcdBacklight);
          //     break;
          case IDX_INVERT_DISPLAY:
            tft.print(config.invertDisplay ? "YES" : "NO");
            break;
          case IDX_LOW_BAT:
            tft.print(config.lowBat, 2);
            break;
          case IDX_FULL_BAT:
            tft.print(config.fullBat, 2);
            break;
          case IDX_SHUTDOWN_COUNTDOWN:
            tft.print(config.shutdownCountdown);
            break;
          case IDX_LD_SIGNAL_INVET:
            tft.print(config.LDSignalInvet ? "YES" : "NO");
            break;
          case IDX_HL_SIGNAL_INVET:
            tft.print(config.HLSignalInvet ? "YES" : "NO");
            break;
          case IDX_SHOW_START:
            tft.print(config.showStart ? "YES" : "NO");
            break;
          case IDX_SHOW_END:
            tft.print(config.showEnd ? "YES" : "NO");
            break;
          case IDX_LCD_REFRESH_CYCLE:
            tft.print(config.TFTrefreshCycle);
            break;
          case IDX_LCD_RGB:
            tft.print(config.LCD_RGB == ST77XX_MADCTL_RGB ? "RGB" : "BGR");
            break;
          case IDX_CALIBRATION_VALUE:
            tft.print(config.calibrationValue);
            break;
        }

        lastValues[currentIdx] = currentValue;
      }
    }
  }
  // }
  lastPage = page;
  lastSetting = currentSetting;
  isSetting = currentSetting;
}

void setup() {
  // Serial.setTx(SERIAL_TX);
  // Serial.setRx(SERIAL_RX);
  // Serial.begin(115200);


  initPin();
  checkConfig();
  // 长按开机判定期间手动复位一次屏幕
  digitalWrite(LCD_RES, LOW);
  delay(300);
  digitalWrite(LCD_RES, HIGH);
  initLCD();
  delay(200);
  initLCD();
  powerOn();

  // 开机动画
  if (config.showStart)
    loading();
  // 注册按键事件
  btnHandler();
  refreshRPM();
  while (digitalRead(POWER_S) == LOW)
    ;

  btnPower.attachClick(btnPowerClick);
  btnPower.attachLongPressStart(btnPowerLongPress);
  btn2.attachClick(btn2Click);
  btn2.attachLongPressStart(btn2LongPress);
  btn3.attachClick(btn3Click);
  btn3.attachLongPressStart(btn3LongPress);

  shutdownTime = millis();
  // Serial.println("setup end");
}

void loop() {
  static long refreshTime;

  if (isPowerOn) {

    // 屏幕刷新速度限制
    long ms = millis();
    // 屏幕刷新
    if ((ms - refreshTime) > config.TFTrefreshCycle) {
      refreshTime = ms;
      if (isSetting == 0) {
        refreshBat();
        if (menu == 1)
          refreshRPM();
        else if (menu == 2) {
          refreshCTR();
        }
        powerOffHandler();
      } else {
        refreshSetting();
      }
    }

    // 按键处理
    btnHandler();
  }

  // 按键tick
  btnPower.tick();
  btn2.tick();
  btn3.tick();
}